用动态规划算法的变形方法——备忘录方法,解决0-1背包问题

使用备忘录方法解决0-1背包问题:
1.跟直接递归很相似,该算法能将递归遇到的子问题的解保存在一个表中,以便下一个递归遇到同样的子问题时快速求解。

2.为了区分一个子问题是否已经求解,可以通过查表的方式来判断,若子问题对应的表中元素的值为一个初始特殊值,说明该子问题还未求解;否则,表明该子问题曾经已求解过,直接获取子问题的解,不需要递归求解该值。

3.备忘录算法与动态规划算法的区别有:

(1)备忘录方法的递归方式是自顶向下,而动态规划算法则是自底向上递归的。

(2)备忘录方法的控制结构与直接递归方法的控制结构相同,而动态规划算法的控制结构与循环迭代的控制结构相同。

(3)备忘录方法为每个解过的子问题建立了备忘录以备需要时查看,避免相同子问题的重复求解;而动态规划算法则是建立一个表格,从最小的子问题开始向上求解较大的子问题,把每一个子问题的解全部填入表格中,直到表格中出现原问题的解为止。它们两者的共同特点是对子问题的求解都只解了一次,并记录了答案。

(4)当一个问题的所有子问题都至少要解一次时,用动态规划比用备忘录方法要好,此时,动态规划算法没有任何多余的计算。当子问题空间中的部分问题可不必求解时,用备忘录方法较有利,因为从其控制结构可以看出,该方法只解那些确实需要求解的子问题。


以下是源代码:

#include "stdafx.h"
#include <iostream>
#include <ctime>
using namespace std;

const int N = 4; //物品数量
const int C = 7; //背包容量
int w[N] = {2,3,5,2};
int v[N] = {6,4,8,4};
int m[N][C+1]; //记录子问题的解的表格
int x[N];

void initial()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j <= C; j++)
		{
			m[i][j] = -1;
		}
	}
}

//以下使用备忘录法求解0-1背包问题(物品重量为整型)
int KnapsackMemoMethod(int i, int capacity)
{
	if(m[i][capacity] != -1)
		return m[i][capacity]; //使用m[i][capacity]需要检查两个下标是否出界
	int result = 0;
	if(i == N-1)
		if(capacity >= w[i])
		{
			m[i][capacity] = v[i];
			return v[i];
		}			
		else
		{
			m[i][capacity] = 0;
			return 0;
		}

	else
	{
		if(capacity >= w[i])
		{
			int selected = KnapsackMemoMethod(i+1,capacity-w[i])+v[i];
			int unselected = KnapsackMemoMethod(i+1,capacity);
			result = selected > unselected ? selected : unselected;
			m[i][capacity] = result;
			return result;
		}
		else //capacity < w[i]
		{
			result = KnapsackMemoMethod(i+1,capacity);
			m[i][capacity] = result;
			return result;
		}
			
	}
}

void Trace(int i, int capacity)
{
	if(i == N-1)
	{	
		if(m[i][capacity] == v[i])
			x[i] = 1;
		else
		{
			x[i] = 0;
		}
		return;
	}
	else
	{		
		if (m[i][capacity] == m[i+1][capacity])
		{
			x[i] = 0;
			Trace(i+1, capacity);
		}
		else
		{
			x[i] = 1;
			Trace(i+1, capacity - w[i]);
		}
	}	
}

int main()
{
        initial();
	cout<<KnapsackMemoMethod(0,C)<<endl;
	Trace(0,C);
	for (int i = 0; i < N; i++)
	{
		cout<<x[i]<<" ";
	}
	cout<<endl;
	system("pause");
	return 0;
}

运行结果如下:

14

1 0 1 0

  • 11
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
0-1背包问题是一个经典的动态规划问题,可以用递归备忘录和动态规划三种方法来求解。 递归递归是最直观的方法,但是由于存在大量的重复计算,效率较低。递归方法的核心思想是将问题转化为规模更小的子问题逐步求解,最终得到原问题的解。 下面是递归方法的Python代码实现: ```python def knapsack_recursion(w, v, c, i): if i == 0 or c == 0: # 背包容量为0或者物品数量为0时,价值为0 return 0 elif w[i] > c: # 当前物品重量大于背包容量,不放入背包 return knapsack_recursion(w, v, c, i-1) else: # 取最大值:放入当前物品或者不放入当前物品 return max(knapsack_recursion(w, v, c, i-1), v[i]+knapsack_recursion(w, v, c-w[i], i-1)) ``` 备忘录: 为了避免递归方法中的重复计算,可以使用备忘录备忘录的核心思想是在递归的过程中,将已经计算过的结果保存下来,下次需要用到时直接返回结果,避免重复计算。 下面是备忘录的Python代码实现: ```python def knapsack_memorandum(w, v, c, i, memo): if memo[i][c] != -1: # 如果已经计算过,直接返回结果 return memo[i][c] if i == 0 or c == 0: memo[i][c] = 0 elif w[i] > c: memo[i][c] = knapsack_memorandum(w, v, c, i-1, memo) else: memo[i][c] = max(knapsack_memorandum(w, v, c, i-1, memo), v[i]+knapsack_memorandum(w, v, c-w[i], i-1, memo)) return memo[i][c] ``` 动态规划: 动态规划是最优解的方法,它的核心思想是将问题划分为多个子问题,通过计算并保存子问题的结果,逐步推导出原问题的解。动态规划通常采用二维数组来保存子问题的结果。 下面是动态规划的Python代码实现: ```python def knapsack_dp(w, v, c, n): dp = [[0]*(c+1) for i in range(n+1)] for i in range(1, n+1): for j in range(1, c+1): if w[i] > j: dp[i][j] = dp[i-1][j] else: dp[i][j] = max(dp[i-1][j], v[i]+dp[i-1][j-w[i]]) return dp[n][c] ``` 以上三种方法的时间复杂度分别为: - 递归:$O(2^n)$ - 备忘录:$O(nc)$ - 动态规划:$O(nc)$ 其中,$n$ 表示物品的数量,$c$ 表示背包的容量。可以发现,递归的复杂度最高,备忘录和动态规划的复杂度相同,但是备忘录需要额外的空间来保存结果。因此,动态规划是最优解的方法
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值